// *************************************************************************************************
//
// MK3D Engine
// --------------------------------------
// Copyright (C) 2007-2011 Zeb (mailto: zebbey@gmail.com)
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// *************************************************************************************************

#include "mk_header.h"
#include "mk_terrain_layer_brush.h"
#include "mk_terrain_region.h"
#include "mk_error.h"
#include "mk_image.h"
#include "mk_entity_terrain.h"
#include "mk_resource_manager.h"
#include "mk_engine.h"
#include "mk_painter.h"

namespace MK3DE
{
	CTerrainLayerBrush::CTerrainLayerBrush()
	{
		_size = 400.0f;
		_powerFactor = 1.0f;
		_x = 0;
		_y = 0;
		_brushImage = NULL;
	}

	CTerrainLayerBrush::~CTerrainLayerBrush()
	{

	}

	void CTerrainLayerBrush::__ProcessChannel(DWORD& color, DWORD& a, DWORD& b, DWORD& c, DWORD d)
	{
		color += d;
		if (color > 255)
		{
			color = 255;
			a = 0;
			b = 0;
			c = 0;
			return;
		}

		DWORD total = a + b + c;
		float fa = (float)a / total;
		float fb = (float)b / total;
		float fc = (float)c / total;
		a -= (DWORD)(fa * d);
		b -= (DWORD)(fb * d);
		c -= (DWORD)(fc * d);

		if (a + b + c + color > 255)
			color = 255 - a - b - c;
	}

	//	void CTerrainLayerBrush::Paint(CTexture2D* pTexture, int layer, float x, float y)
	//	{
	//		MK_PROCESS_ERROR(pTexture->GetFormat() == PF_A8R8G8B8);
	//		MK_PROCESS_ERROR(layer <= 3 && layer >= 0);
	//
	//		float half = _size * 0.5f;
	//		float halfSqr = half * half;
	//
	//		DWORD width = pTexture->GetWidth();
	//		DWORD height = pTexture->GetHeight();
	//
	//		float fx = (float)(TERRAIN_REGION_WIDTH_VERTEX + 1) / width;
	//		float fy = (float)(TERRAIN_REGION_HEIGHT_VERTEX + 1) / height;
	//
	//		BYTE* pData = NULL;
	//		DWORD pitch = 0;
	//		BOOL ret = pTexture->LockRect((void**)&pData, &pitch, NULL);
	//		MK_PROCESS_ERROR(ret);
	//
	//		for (DWORD ty = 0; ty < height; ++ty)
	//		{
	//			for (DWORD tx = 0; tx < width; ++tx)
	//			{
	//				DWORD vx = (DWORD)(tx * fx * TERRAIN_CELL_WIDTH);
	//				DWORD vy = (DWORD)(ty * fy * TERRAIN_CELL_HEIGHT);
	//				float lenSqr = (vx - x) * (vx - x) + (vy - y) * (vy - y);
	//				if (lenSqr <= halfSqr)
	//				{
	//					DWORD d = (DWORD)(-lenSqr * 255 / halfSqr + 255);
	//
	//					DWORD* color = (DWORD*)(pData + ty * pitch + tx * 4);
	//					DWORD r = (((*color) & 0x00ff0000) >> 16);
	//					DWORD g = (((*color) & 0x0000ff00) >> 8);
	//					DWORD b = ((*color) & 0x000000ff);
	//					DWORD a = (*color) >> 24;
	//
	//					if (layer == 0)
	//						__ProcessChannel(r, g, b, a, d);
	//					else if (layer == 1)
	//						__ProcessChannel(g, r, b, a, d);
	//					else if (layer == 2)
	//						__ProcessChannel(b, r, g, a, d);
	//					else if (layer == 3)
	//						__ProcessChannel(a, r, g, b, d);
	//
	//					*color = D3DCOLOR_ARGB(a, r, g, b);
	//				}
	//			}
	//		}
	//
	//		pTexture->Unlock();
	//
	//Exit0:
	//		return;
	//	}
	//
	//
	void CTerrainLayerBrush::Paint(CTexture2D* pTexture, int layer, float x, float y)
	{
		MK_PROCESS_ERROR(pTexture->GetFormat() == PF_A8R8G8B8);
		MK_PROCESS_ERROR(layer <= 3 && layer >= 0);

		float half = _size * 0.5f;
		float left = x - half;
		float top = y - half;

		BYTE* imageData = _brushImage->GetData();
		DWORD widthBrush = _brushImage->GetWidth();
		DWORD heightBrush = _brushImage->GetHeight();
		ASSERT(imageData);
		ASSERT(widthBrush);
		ASSERT(heightBrush);

		DWORD widthBlendTexture = pTexture->GetWidth();
		DWORD heightBlendTexture = pTexture->GetHeight();

		float fx = (float)(TERRAIN_REGION_WIDTH) / (widthBlendTexture - 2);
		float fy = (float)(TERRAIN_REGION_HEIGHT) / (heightBlendTexture - 2);

		BYTE* pData = NULL;
		DWORD pitch = 0;
		BOOL ret = pTexture->LockRect((void**)&pData, &pitch, NULL);
		MK_PROCESS_ERROR(ret);

		for (DWORD ty = 0; ty < heightBlendTexture; ++ty)
		{
			for (DWORD tx = 0; tx < widthBlendTexture; ++tx)
			{
				float vx = tx * fx * TERRAIN_CELL_WIDTH;
				float vy = ty * fy * TERRAIN_CELL_HEIGHT;
				float dx = vx - left;
				float dy = vy - top;

				if (dx > 0 && dx < _size && dy > 0 && dy < _size)
				{
					float tu = dx / _size;
					float tv = dy / _size;

					DWORD imageX = (DWORD)(tu * (float)widthBrush + 0.5f);
					DWORD imageY = (DWORD)(tv * (float)heightBrush + 0.5f);
					if (imageX < widthBrush && imageY < heightBrush)
					{
						BYTE pixelData = imageData[imageY * widthBrush + imageX];
						DWORD power = (DWORD)((float)pixelData * _powerFactor);
						DWORD* color = (DWORD*)(pData + ty * pitch + tx * 4);
						DWORD r = (((*color) & 0x00ff0000) >> 16);
						DWORD g = (((*color) & 0x0000ff00) >> 8);
						DWORD b = ((*color) & 0x000000ff);
						DWORD a = (*color) >> 24;

						if (layer == 0)
							__ProcessChannel(r, g, b, a, power);
						else if (layer == 1)
							__ProcessChannel(g, r, b, a, power);
						else if (layer == 2)
							__ProcessChannel(b, r, g, a, power);
						else if (layer == 3)
							__ProcessChannel(a, r, g, b, power);

						*color = D3DCOLOR_ARGB(a, r, g, b);

					}
				}
			}
		}

		pTexture->Unlock();

Exit0:
		return;
	}

	void CTerrainLayerBrush::Render(CEntityTerrain* terrain)
	{
		CPainter* painter = GetEngine()->GetPainter();
		float left = _x - _size * 0.5f;
		float right = _x + _size * 0.5f;
		float top = _y - _size * 0.5f;
		float bottom = _y + _size * 0.5f;

		// draw up line.
		float x1 = left;
		float z1 = top;
		while (true)
		{
			float x2 = x1 + 20.0f;
			float z2 = top;
			float y1 = 0, y2 = 0;
			BOOL end = FALSE;
			if (x2 > right - MK_EPSILON)
			{
				x2 = right;
				end = TRUE;
			}

			terrain->GetGroundHeight(y1, x1, z1);
			terrain->GetGroundHeight(y2, x2, z2);
			painter->DrawLine3D(D3DXVECTOR3(x1, y1 + 5.0f, z1), D3DXVECTOR3(x2, y2 + 5.0f, z2), 0xffffffff, 0xffffffff);

			if (end)
				break;

			x1 = x2;
		}

		// draw left line.
		x1 = left;
		z1 = top;
		while (true)
		{
			float x2 = x1;
			float z2 = z1 + 20.0f;
			float y1 = 0, y2 = 0;
			BOOL end = FALSE;
			if (z2 > bottom - MK_EPSILON)
			{
				z2 = bottom;
				end = TRUE;
			}

			terrain->GetGroundHeight(y1, x1, z1);
			terrain->GetGroundHeight(y2, x2, z2);
			painter->DrawLine3D(D3DXVECTOR3(x1, y1 + 5.0f, z1), D3DXVECTOR3(x2, y2 + 5.0f, z2), 0xffffffff, 0xffffffff);

			if (end)
				break;

			z1 = z2;
		}

		// draw right line.
		x1 = right;
		z1 = top;
		while (true)
		{
			float x2 = x1;
			float z2 = z1 + 20.0f;
			float y1 = 0, y2 = 0;
			BOOL end = FALSE;
			if (z2 > bottom - MK_EPSILON)
			{
				z2 = bottom;
				end = TRUE;
			}

			terrain->GetGroundHeight(y1, x1, z1);
			terrain->GetGroundHeight(y2, x2, z2);
			painter->DrawLine3D(D3DXVECTOR3(x1, y1 + 5.0f, z1), D3DXVECTOR3(x2, y2 + 5.0f, z2), 0xffffffff, 0xffffffff);

			if (end)
				break;

			z1 = z2;
		}

		// draw bottom line.
		x1 = left;
		z1 = bottom;
		while (true)
		{
			float x2 = x1 + 20.0f;
			float z2 = bottom;
			float y1 = 0, y2 = 0;
			BOOL end = FALSE;
			if (x2 > right - MK_EPSILON)
			{
				x2 = right;
				end = TRUE;
			}

			terrain->GetGroundHeight(y1, x1, z1);
			terrain->GetGroundHeight(y2, x2, z2);
			painter->DrawLine3D(D3DXVECTOR3(x1, y1 + 5.0f, z1), D3DXVECTOR3(x2, y2 + 5.0f, z2), 0xffffffff, 0xffffffff);

			if (end)
				break;

			x1 = x2;
		}
	}


	BOOL CTerrainLayerBrush::LoadImage(const char* fileName, DWORD width, DWORD height)
	{
		BOOL ret = FALSE;
		CResourceManager* resourceManger = GetEngine()->GetResourceManager();
		ret = resourceManger->GetImage(fileName, _brushImage);
		MK_PROCESS_ERROR(ret);

		ret = _brushImage->ConvertFormat(PF_L8);
		MK_PROCESS_ERROR(ret);

		ret = TRUE;
Exit0:
		return ret;
	}
}